// Copyright  2004-2008 Backplane Ltd.
//
// This file is part of Ubiquity formsPlayer
//
// Ubiquity formsPlayer is free software; you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by the
// Free Software Foundation; either version 3 of the License, or (at your
// option) any later version.
//
// Ubiquity formsPlayer is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
// more details.
//
// You should have received a copy of the GNU General Public License
// along with Ubiquity formsPlayer If not, see <http://www.gnu.org/licenses/>.
//	
//
//   Purpose: Implementation of CImplActionMessage.
//   $Id$

#include "stdafx.h"
#include "actionmessage.h"
#include "XformsElementMessage.h"
#include "XFormsApp.h"
#include <Common/MessageCodes/msw/MessageCodes.h>
#include "singlenodebindingondemand.h"
#include "ImplBind.h"

extern CXFormsApp theApp;

//Error definitions:
const char * EMSG_MSG_NOLEVEL =  "required attribute 'level' on xforms:message not specified";

CImplActionMessage::CImplActionMessage()
: m_eLevel(ELevels::modal), m_sTextContent(""), m_bTargetIsSystray(false),m_bIsShowing(false)
,m_pRefEvaluator(0),m_pBind(0)
{
}

CImplActionMessage::CImplActionMessage(CXFormsElementMessage* pXFEl)	
: m_eLevel(ELevels::modal), m_sTextContent(""), m_bTargetIsSystray(false),m_bIsShowing(false)
,m_pRefEvaluator(0),m_pBind(0)
{
	m_pXFormsElement = pXFEl;
	pXFEl->AddRef();
}

CImplActionMessage::~CImplActionMessage()
{
	delete m_pRefEvaluator;
	m_pXFormsElement->formControlUnInitialize();
}


HRESULT CImplActionMessage::PerformAction()
{
	HRESULT hr = E_FAIL;
	CComBSTR sMessage;
	GetTextValue(&sMessage);
	
	if(m_pXFormsElement && !m_bTargetIsSystray && !m_bIsShowing)
	{
		hr = m_pXFormsElement->put_value(sMessage);
		FireEvent(EVENT_ACTIVATE);
		m_bIsShowing = true;
		if(m_eLevel == ELevels::modal)
			m_pXForms->activateModalMessage();
	}



	// Check whether this message is targeted at the system tray
	if(m_bTargetIsSystray)
		SystrayInvoke();

	return hr;
}

CImplActionMessage::ELevels CImplActionMessage::getLevel() 
{
	return m_eLevel;
}

void CImplActionMessage::GetTextValue(BSTR * psValue)
{
	if(!m_pBind && !m_pRefEvaluator && !m_sTextContent)
		ResolveBindings();
	if(m_pBind)
	{
		CComPtr<IXMLDOMNode> pNode;
		m_pBind->GetNodeAt(0,&pNode);
		pNode->get_text(psValue);
	}
	else if(m_pRefEvaluator)
	{
		CComPtr<IXMLDOMNode> pContext;		
		GetXPathContext(&pContext);
		if(pContext)
		{
			CComPtr<IXMLDOMNode> pNode;
			m_pRefEvaluator->GetNode(pContext,&pNode);
#ifdef _DEBUG
if(false)
{
	CComBSTR s;
	pContext->get_xml(&s);
}
#endif
			if(pNode)
				pNode->get_text(psValue);
		}
	}
	else
	{
		m_sTextContent.CopyTo(psValue);
	}

}

// Bug 987: Allow messages to target the system tray.
HRESULT CImplActionMessage::SystrayInvoke(void)
{
	HRESULT hr = E_NOTIMPL;

/*	CXFormsXFormsElement *pXF = m_pXForms;
	if(pXF) {
		// Get the co-ordinates of the system tray and our icon.
		ISysTrayIcon *pSysTray = pXF->DefaultSystray();
		if(pSysTray) {
			pSysTray->AddRef();
			RECT rIconPosition, rTrayPosition;
			rIconPosition.bottom = rIconPosition.left = rIconPosition.right = rIconPosition.top = rTrayPosition.bottom = rTrayPosition.left = rTrayPosition.right = rTrayPosition.top = 0;
			pSysTray->GetPosition(&rIconPosition.left, &rIconPosition.top, &rIconPosition.right, &rIconPosition.bottom);
			pSysTray->GetTrayPosition(&rTrayPosition.left, &rTrayPosition.top, &rTrayPosition.right, &rTrayPosition.bottom);

			// Calculate the size, position and animation parameters of the message.
			int nIconWidth = (rIconPosition.right > rIconPosition.left ? rIconPosition.right - rIconPosition.left : rIconPosition.left - rIconPosition.right);
			int nScreenWidth = ::GetSystemMetrics(SM_CXSCREEN);
			int nScreenHeight = ::GetSystemMetrics(SM_CYSCREEN);
			long nMessageWidth = 180, nMessageHeight = 110, nAnimateFlags = RENDERER_ANIMATE_SHOW | RENDERER_ANIMATE_SLIDE, nAnimateTime = 200;
			// Copy the current width and height styles from the master element, if possible.
			CXFormsElement *pXFEl = m_pXFormsElement;
			//CComQIPtr<IHTMLElement2> pEl2 = pXFEl;
			CComPtr<IHTMLElement2> pEl2;
			if(pXFEl)
				pXFEl->QueryInterface(IID_IHTMLElement2, reinterpret_cast<void **>(&pEl2));
			if(pEl2) {
				// Get the current style object.
				CComPtr<IHTMLCurrentStyle> pCurrStyle;
				pEl2->get_currentStyle(&pCurrStyle);
				if(pCurrStyle) {
					// Query for width and height settings.
					CComVariant vWidth, vHeight;
					pCurrStyle->get_width(&vWidth);
					pCurrStyle->get_height(&vHeight);
					if(vWidth.vt == VT_BSTR && vWidth.bstrVal) {
						// We have a width setting.
						unsigned int nLength = wcslen(vWidth.bstrVal);
						if((vWidth.bstrVal[--nLength] == L'x' || vWidth.bstrVal[nLength] == L'X') && (vWidth.bstrVal[--nLength] == L'p' || vWidth.bstrVal[nLength] == L'P')) {
							wchar_t *sVal = reinterpret_cast<wchar_t *>(malloc(sizeof(wchar_t) * (nLength + 1)));
							if(sVal) {
								memcpy(sVal, vWidth.bstrVal, sizeof(wchar_t) * nLength);
								sVal[nLength] = L'\0';
								nMessageWidth = _wtol(sVal);
								if(nMessageWidth < 0)
									nMessageWidth = 180;
								free(sVal);
							}
						}
					}
					if(vHeight.vt == VT_BSTR && vHeight.bstrVal) {
						// We have a height setting.
						unsigned int nLength = wcslen(vHeight.bstrVal);
						if((vHeight.bstrVal[--nLength] == L'x' || vHeight.bstrVal[nLength] == L'X') && (vHeight.bstrVal[--nLength] == L'p' || vHeight.bstrVal[nLength] == L'P')) {
							wchar_t *sVal = reinterpret_cast<wchar_t *>(malloc(sizeof(wchar_t) * (nLength + 1)));
							if(sVal) {
								memcpy(sVal, vHeight.bstrVal, sizeof(wchar_t) * nLength);
								sVal[nLength] = L'\0';
								nMessageHeight = _wtol(sVal);
								if(nMessageHeight < 0)
									nMessageHeight = 110;
								free(sVal);
							}
						}
					}
				}
			}
			// Set the horizontal position of the message.
			long nMessageX = (rIconPosition.left + (nIconWidth / 2)) - (nMessageWidth / 2), nMessageY;
			if(nMessageX <= 0)
				nMessageX = 1;
			else if(nMessageX + nMessageWidth >= nScreenWidth)
				nMessageX = nScreenWidth - nMessageWidth - 1;
			// Set the vertical postion of the message.
			if(rTrayPosition.bottom < nScreenHeight / 2) {
				nMessageY = rTrayPosition.bottom + 1;
				nAnimateFlags |= RENDERER_ANIMATE_YNEGATIVE;
			} else {
				nMessageY = rTrayPosition.top - nMessageHeight - 1;
				nAnimateFlags |= RENDERER_ANIMATE_YPOSITIVE;
			}

			// Get a pointer to the owner document...
			CComPtr<IHTMLDocument2> pOwnerDoc;
			pXF->GetOwnerDocument(&pOwnerDoc);
			if(pOwnerDoc) {
				// ...which we use to get a pointer to the parent browser object.
				CComPtr<IWebBrowser2> pBrowser;
				theApp.FormsManager().GetBrowserFromDocument(pOwnerDoc, &pBrowser);
				CComVariant vTarget = pBrowser;
				// Now instantiate a renderer.
				IUnknown *pUnk = 0; // Because the renderer is responsible for destroying itself in this instance, this pointer will not be released.
				xport::GetFirstImplementation(CComBSTR(L"ui.perceivableunit.renderer"), CComBSTR(L"1.0"), &pUnk, &theApp.FormsManager().m_pDIR.p);
				CComQIPtr<IRenderer> pRen = pUnk;
				if(pRen) {
					// Set a unique identifier for the renderer.
					CStringW sId;
					sId.Format(L"FP_MESSAGE_SYSTRAY_%X", this);
					// Initialise the renderer.
					pRen->Initialise(0, RENDERER_TYPE_UNFRAMEDMESSAGE, nMessageX, nMessageY, nMessageWidth, nMessageHeight, CComBSTR(sId), 0, 0, vTarget);
					// Get the message window handle.
					long nWindowId;
					pRen->get_WindowId(&nWindowId);
					// Inform it of our animation settings.
					::SendMessage(reinterpret_cast<HWND>(nWindowId), WM_REN_SETANIMATION, nAnimateTime, nAnimateFlags);
					// Load the message into the renderer
					CComBSTR sMessage;
					GetTextValue(&sMessage);
					if(!sMessage)
						sMessage = m_sContent;
					CStringW sHTML;
					sHTML.Format(L"<html xmlns=\"http://www.w3.org/1999/xhtml\"><head><style type=\"text/css\">body { background-color: #FFFFFF; color: #000000; font-family: \"Trebuchet MS\", Verdana, Helvetica, sans-serif; font-size: x-small; margin-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; border: 3px solid black; text-align: center; } h1 { font-weight: bold; font-size: x-small; }</style></head><body scroll=\"no\">%s</body></html>", sMessage);
					HRESULT hr2 = pRen->LoadXML(CComBSTR(sHTML), 0);
					// Redirect all navigation in the message to the parent browser.
					pRen->Restrict(VARIANT_TRUE);
					if(m_eLevel == ephemeral && SUCCEEDED(hr2))
						// Set the timer for the message 
						::SetTimer(reinterpret_cast<HWND>(nWindowId), RENDERER_TIMER_AUTOHIDE, 5000, 0);
				}
				// The renderer destroys itself so, we deliberately don't decremement this ref count.
				//if(pUnk)
				//	pUnk->Release();
			}
			pSysTray->Release();
		}
	}*/
	return hr;
}


void CImplActionMessage::ResolveBindings() 
{
	
	ResolveEvaluationContext();
	//bind takes precedence
	if(m_sBind)
	{	
		if(m_pXForms)
			m_pXForms->getBind(m_sBind,m_pBind);
	}
	else if(m_sRef)
	{
		if(m_pModel && !m_pRefEvaluator)
			m_pRefEvaluator = new CSingleNodeBindingOnDemand(m_pModel,m_sRef);
	}
}